﻿using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.RateLimiting;
using Sgr.AspNetCore.ActionFilters.Permissions;
using Sgr.Oss.Services;
using Sgr.Oss.ViewModels;

//1.	获取存储桶列表 - 通过 GET /api/v1/sgr/oss/buckets 接口获取所有存储桶
//2.	查询对象列表 - 通过 GET /api/v1/sgr/oss/buckets/{bucketName}/objects 接口查询指定存储桶中的对象列表
//3.	上传文件 - 通过 POST /api/v1/sgr/oss/buckets/{bucketName}/objects?objectPath=xxx 接口上传文件
//4.	查询文件元数据 - 通过 GET /api/v1/sgr/oss/buckets/{bucketName}/objects/metadata?objectPath=xxx 接口获取文件元数据
//5.	删除文件 - 通过 DELETE /api/v1/sgr/oss/buckets/{bucketName}/objects?objectPath=xxx 接口删除文件
//6.	分享文件 - 通过 GET /api/v1/sgr/oss/buckets/{bucketName}/objects/share?objectPath=xxx&expiryMinutes=60 接口生成分享链接

namespace Sgr.Oss.Controllers
{
    /// <summary>
    /// 对象存储服务接口
    /// </summary>
    [Route("api/v1/sgr/oss")]
    [ApiController]
    [EnableRateLimiting("RateLimiterPolicy.Sgr.Oss")]
    [Tags("Oss")]
    public class OssController : ControllerBase
    {
        private readonly IOssService _ossService;

        /// <summary>
        /// 初始化对象存储控制器
        /// </summary>
        /// <param name="ossService">对象存储服务</param>
        /// <exception cref="ArgumentNullException">当参数为空时抛出</exception>
        public OssController(IOssService ossService)
        {
            _ossService = ossService ?? throw new ArgumentNullException(nameof(ossService));
        }

        /// <summary>
        /// 获取存储桶列表
        /// </summary>
        /// <remarks>
        /// 返回系统中所有存储桶名称的列表。
        /// </remarks>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>存储桶名称列表</returns>
        /// <response code="200">成功获取存储桶列表</response>
        /// <response code="401">权限不足</response>
        /// <response code="429">请求过于频繁</response>
        /// <response code="500">服务器内部错误</response>
        [CheckPermission(Permissions.QueryOss)]
        [HttpGet("buckets")]
        [ProducesResponseType(typeof(IEnumerable<string>), StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status429TooManyRequests)]
        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
        public async Task<ActionResult<IEnumerable<string>>> GetBuckets(CancellationToken cancellationToken = default)
        {
            try
            {
                var bucketNames = await _ossService.GetBucketNamesAsync(cancellationToken);
                return Ok(bucketNames);
            }
            catch (Exception ex)
            {
                return StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
            }
        }

        /// <summary>
        /// 列举存储桶中的对象
        /// </summary>
        /// <remarks>
        /// 根据存储桶名称查询存储桶中的对象列表，支持分页和前缀过滤。
        /// </remarks>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="prefix">前缀过滤条件（可选）</param>
        /// <param name="continuationToken">分页延续标记（可选）</param>
        /// <param name="maxKeys">单页最大返回数量（默认1000）</param>
        /// <param name="useHierarchy">是否使用层级结构（默认true）</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>对象列表和分页信息</returns>
        /// <response code="200">成功获取对象列表</response>
        /// <response code="401">权限不足</response>
        /// <response code="404">存储桶不存在</response>
        /// <response code="429">请求过于频繁</response>
        /// <response code="500">服务器内部错误</response>
        [CheckPermission(Permissions.QueryOss)]
        [HttpGet("buckets/{bucketName}/objects")]
        [ProducesResponseType(typeof(ListObjectsResult), StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        [ProducesResponseType(StatusCodes.Status429TooManyRequests)]
        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
        public async Task<ActionResult<ListObjectsResult>> ListObjects(
            string bucketName,
            [FromQuery] string? prefix = null,
            [FromQuery] string? continuationToken = null,
            [FromQuery] int maxKeys = 1000,
            [FromQuery] bool useHierarchy = true,
            CancellationToken cancellationToken = default)
        {
            try
            {
                // 检查存储桶是否存在
                if (!await _ossService.BucketAnyAsync(bucketName, cancellationToken))
                {
                    return NotFound($"存储桶 '{bucketName}' 不存在");
                }

                var result = await _ossService.ListObjectsAsync(
                    bucketName,
                    prefix,
                    continuationToken,
                    maxKeys,
                    useHierarchy,
                    cancellationToken);

                return Ok(result);
            }
            catch (Exception ex)
            {
                return StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
            }
        }

        /// <summary>
        /// 上传文件到对象存储
        /// </summary>
        /// <remarks>
        /// 将文件上传到指定的存储桶和路径。可以通过添加 'x-sgr-meta-' 前缀的HTTP头来添加自定义元数据。
        /// </remarks>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="objectPath">对象路径</param>
        /// <param name="acl">访问控制级别（默认为继承）</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>上传成功的对象信息</returns>
        /// <response code="201">文件上传成功</response>
        /// <response code="400">请求参数错误或上传失败</response>
        /// <response code="429">请求过于频繁</response>
        /// <response code="500">服务器内部错误</response>
        [CheckPermission(Permissions.CreateAndUpdateOss)]
        [HttpPost("buckets/{bucketName}/objects")]
        [ProducesResponseType(StatusCodes.Status201Created)]
        [ProducesResponseType(StatusCodes.Status429TooManyRequests)]
        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
        public async Task<ActionResult> UploadObject(
            string bucketName,
            [FromQuery] string objectPath,
            [FromQuery] ObjectAcl acl = ObjectAcl.Inherited,
            CancellationToken cancellationToken = default)
        {
            try
            {
                if (string.IsNullOrEmpty(objectPath))
                {
                    return BadRequest("对象路径不能为空");
                }

                // 检查存储桶是否存在，如不存在则创建
                if (!await _ossService.BucketAnyAsync(bucketName, cancellationToken))
                {
                    if (!await _ossService.CreateBucketAsync(bucketName, BucketAcl.Private, cancellationToken))
                    {
                        return BadRequest($"无法创建存储桶 '{bucketName}'");
                    }
                }

                // 收集元数据
                var metadata = new Dictionary<string, string>();
                foreach (var header in Request.Headers)
                {
                    if (header.Key.StartsWith("x-sgr-meta-", StringComparison.OrdinalIgnoreCase))
                    {
                        metadata[header.Key["x-sgr-meta-".Length..]] = header.Value.ToString();
                    }
                }

                // 从请求流上传文件
                var result = await _ossService.PutObjectAsync(
                    bucketName,
                    objectPath,
                    Request.Body,
                    metadata,
                    acl,
                    cancellationToken: cancellationToken);

                if (result)
                {
                    // 获取对象元数据
                    var objMetadata = await _ossService.GetObjectMetadataAsync(bucketName, objectPath, cancellationToken);

                    // 生成一个默认的预签名URL（有效期如1小时）
                    var expiry = TimeSpan.FromHours(1);
                    var presignedUrl = await _ossService.GetPresignedGetUrlAsync(bucketName, objectPath, expiry, cancellationToken);

                    return Created(presignedUrl, new OssUploadModel
                    {
                        Bucket = bucketName,
                        ObjectPath = objectPath,
                        Size = objMetadata.Size,
                        ETag = objMetadata.ETag,
                        LastModified = objMetadata.LastModified,
                        Url = presignedUrl,                                         // 添加完整的预签名URL
                        Expiry = DateTime.UtcNow.Add(expiry).ToIso8601String()      // 添加过期时间
                    });
                }
                else
                {
                    return BadRequest("上传文件失败");
                }
            }
            catch (Exception ex)
            {
                return StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
            }
        }

        /// <summary>
        /// 获取对象元数据
        /// </summary>
        /// <remarks>
        /// 获取指定对象的详细元数据信息，包括大小、最后修改时间、Content-Type和ETag等。
        /// </remarks>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="objectPath">对象路径</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>对象元数据</returns>
        /// <response code="200">成功获取对象元数据</response>
        /// <response code="400">请求参数错误</response>
        /// <response code="404">对象不存在</response>
        /// <response code="429">请求过于频繁</response>
        /// <response code="500">服务器内部错误</response>
        [CheckPermission(Permissions.QueryOss)]
        [HttpGet("buckets/{bucketName}/objects/metadata")]
        [ProducesResponseType(typeof(ObjectMetadata), StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        [ProducesResponseType(StatusCodes.Status429TooManyRequests)]
        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
        public async Task<ActionResult<ObjectMetadata>> GetObjectMetadata(
            string bucketName,
            [FromQuery] string objectPath,
            CancellationToken cancellationToken = default)
        {
            try
            {
                if (string.IsNullOrEmpty(objectPath))
                {
                    return BadRequest("对象路径不能为空");
                }

                // 检查对象是否存在
                if (!await _ossService.ObjectsAnyAsync(bucketName, objectPath, cancellationToken))
                {
                    return NotFound($"对象 '{objectPath}' 不存在于存储桶 '{bucketName}' 中");
                }

                var metadata = await _ossService.GetObjectMetadataAsync(bucketName, objectPath, cancellationToken);
                return Ok(metadata);
            }
            catch (Exception ex)
            {
                return StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
            }
        }

        /// <summary>
        /// 删除对象
        /// </summary>
        /// <remarks>
        /// 从指定存储桶中删除对象文件。
        /// </remarks>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="objectPath">对象路径</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>删除操作结果</returns>
        /// <response code="200">文件删除成功</response>
        /// <response code="400">请求参数错误或删除失败</response>
        /// <response code="404">对象不存在</response>
        /// <response code="429">请求过于频繁</response>
        /// <response code="500">服务器内部错误</response>
        [CheckPermission(Permissions.DeleteOss)]
        [HttpDelete("buckets/{bucketName}/objects")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        [ProducesResponseType(StatusCodes.Status429TooManyRequests)]
        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
        public async Task<ActionResult> DeleteObject(
            string bucketName,
            [FromQuery] string objectPath,
            CancellationToken cancellationToken = default)
        {
            try
            {
                if (string.IsNullOrEmpty(objectPath))
                {
                    return BadRequest("对象路径不能为空");
                }

                // 检查对象是否存在
                if (!await _ossService.ObjectsAnyAsync(bucketName, objectPath, cancellationToken))
                {
                    return NotFound($"对象 '{objectPath}' 不存在于存储桶 '{bucketName}' 中");
                }

                var result = await _ossService.RemoveObjectAsync(bucketName, objectPath, cancellationToken);
                if (result)
                {
                    return Ok(new { Message = $"对象 '{objectPath}' 已成功删除" });
                }
                else
                {
                    return BadRequest("删除文件失败");
                }
            }
            catch (Exception ex)
            {
                return StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
            }
        }

        /// <summary>
        /// 生成文件分享链接
        /// </summary>
        /// <remarks>
        /// 为指定对象生成带有时效性的预签名URL，用于临时分享文件。
        /// </remarks>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="objectPath">对象路径</param>
        /// <param name="expiryMinutes">过期时间（分钟），默认60分钟</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>分享链接和过期时间</returns>
        /// <response code="200">成功生成分享链接</response>
        /// <response code="401">权限不足</response>
        /// <response code="400">请求参数错误</response>
        /// <response code="404">对象不存在</response>
        /// <response code="429">请求过于频繁</response>
        /// <response code="500">服务器内部错误</response>
        [CheckPermission(Permissions.QueryOss)]
        [HttpGet("buckets/{bucketName}/objects/share")]
        [ProducesResponseType(typeof(OssPresignedObjectModel), StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        [ProducesResponseType(StatusCodes.Status429TooManyRequests)]
        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
        public async Task<ActionResult<OssPresignedObjectModel>> ShareObject(
            string bucketName,
            [FromQuery] string objectPath,
            [FromQuery] int expiryMinutes = 60,
            CancellationToken cancellationToken = default)
        {
            try
            {
                if (string.IsNullOrEmpty(objectPath))
                {
                    return BadRequest("对象路径不能为空");
                }

                // 限制过期时间范围
                if (expiryMinutes <= 0 || expiryMinutes > 10080) // 最大7天 (10080分钟)
                {
                    return BadRequest("过期时间必须在1分钟到7天之间");
                }

                // 检查对象是否存在
                if (!await _ossService.ObjectsAnyAsync(bucketName, objectPath, cancellationToken))
                {
                    return NotFound($"对象 '{objectPath}' 不存在于存储桶 '{bucketName}' 中");
                }

                // 生成预签名URL
                var expiry = TimeSpan.FromMinutes(expiryMinutes);
                var url = await _ossService.GetPresignedGetUrlAsync(bucketName, objectPath, expiry, cancellationToken);

                return Ok(new OssPresignedObjectModel
                {
                    Url = url,
                    Expiry = DateTime.UtcNow.Add(expiry).ToIso8601String()
                });
            }
            catch (Exception ex)
            {
                return StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
            }
        }

        /// <summary>
        /// 生成用于上传的预签名URL
        /// </summary>
        /// <remarks>
        /// 生成用于上传文件的预签名URL，支持指定内容类型和自定义元数据。
        /// URL在指定的过期时间内有效，可用于客户端直接上传文件。
        /// </remarks>
        /// <param name="bucketName">存储桶名称</param>
        /// <param name="objectPath">对象路径</param>
        /// <param name="contentType">内容类型（可选）</param>
        /// <param name="expiryMinutes">过期时间（分钟），默认60分钟</param>
        /// <param name="cancellationToken">取消令牌</param>
        /// <returns>预签名上传URL和过期时间</returns>
        /// <response code="200">成功生成上传URL</response>
        /// <response code="400">请求参数错误</response>
        /// <response code="401">权限不足</response>
        /// <response code="404">存储桶不存在</response>
        /// <response code="429">请求过于频繁</response>
        /// <response code="500">服务器内部错误</response>
        [CheckPermission(Permissions.QueryOss)]
        [HttpGet("buckets/{bucketName}/objects/presigned-put")]
        [ProducesResponseType(typeof(OssPresignedObjectModel), StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status404NotFound)]
        [ProducesResponseType(StatusCodes.Status429TooManyRequests)]
        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
        public async Task<ActionResult<OssPresignedObjectModel>> GetPresignedPutUrl(
            string bucketName,
            [FromQuery] string objectPath,
            [FromQuery] string? contentType = null,
            [FromQuery] int expiryMinutes = 60,
            CancellationToken cancellationToken = default)
        {
            try
            {
                if (string.IsNullOrEmpty(objectPath))
                {
                    return BadRequest("对象路径不能为空");
                }

                // 限制过期时间范围
                if (expiryMinutes <= 0 || expiryMinutes > 10080) // 最大7天 (10080分钟)
                {
                    return BadRequest("过期时间必须在1分钟到7天之间");
                }

                // 检查存储桶是否存在，如不存在则创建
                if (!await _ossService.BucketAnyAsync(bucketName, cancellationToken))
                {
                    if (!await _ossService.CreateBucketAsync(bucketName, BucketAcl.Private, cancellationToken))
                    {
                        return BadRequest($"无法创建存储桶 '{bucketName}'");
                    }
                }

                // 收集元数据
                var metadata = new Dictionary<string, string>();
                foreach (var header in Request.Headers)
                {
                    if (header.Key.StartsWith("x-sgr-meta-", StringComparison.OrdinalIgnoreCase))
                    {
                        metadata[header.Key["x-sgr-meta-".Length..]] = header.Value.ToString();
                    }
                }

                // 生成预签名URL
                var expiry = TimeSpan.FromMinutes(expiryMinutes);
                var url = await _ossService.GetPresignedPutUrlAsync(
                    bucketName,
                    objectPath,
                    expiry,
                    contentType,
                    metadata,
                    cancellationToken);

                return Ok(new OssPresignedObjectModel
                {
                    Url = url,
                    Expiry = DateTime.UtcNow.Add(expiry).ToIso8601String()
                });
            }
            catch (Exception ex)
            {
                return StatusCode(StatusCodes.Status500InternalServerError, ex.Message);
            }
        }
    }

    /// <summary>
    /// 用于支持文件流回调的自定义结果类型
    /// </summary>
    public class FileCallbackResult : FileResult
    {
        private readonly Func<Stream, HttpContext, Task> _callback;

        public FileCallbackResult(string contentType, Func<Stream, HttpContext, Task> callback)
            : base(contentType)
        {
            _callback = callback ?? throw new ArgumentNullException(nameof(callback));
        }

        public override async Task ExecuteResultAsync(ActionContext context)
        {
            var response = context.HttpContext.Response;

            // 设置Content-Type
            response.ContentType = ContentType;

            // 执行回调，将文件内容写入响应流
            await _callback(response.Body, context.HttpContext);
        }
    }
}